﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace SimpleFileManager.Manager
{
    public partial class FileManager
    {
        public interface IFilePanelMemento
        {
            void Restore(FilePanel filePanel);
        }

        public class FilePanel
        {
            //текущий каталог
            private string _currentFolder = Environment.CurrentDirectory;

            private IFolderIterator _iterator;

            private FileManager _fileManager;

            //история посещения каталогов
            private Stack<string> _foldersBack = new Stack<string>();
            private Stack<string> _foldersForward = new Stack<string>();

            //отслеживание событий в текущей папке
            private FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Environment.CurrentDirectory);

            [Serializable]
            private class FilePanelMemento : IFilePanelMemento
            {
                private readonly string _currentFolder;
                private readonly Stack<string> _foldersBack;
                private readonly Stack<string> _foldersForward;

                public FilePanelMemento(FilePanel filePanel)
                {
                    _currentFolder = filePanel._currentFolder;
                    _foldersBack = filePanel._foldersBack;
                    _foldersForward = filePanel._foldersForward;
                }

                public void Restore(FilePanel filePanel)
                {
                    filePanel._currentFolder = _currentFolder;
                    filePanel._foldersBack = _foldersBack;
                    filePanel._foldersForward = _foldersForward;
                }
            }

            public string CurrentFolder
            {
                get
                {
                    return _currentFolder;
                }
            }

            public FilePanel(FileManager fileManager)
            {
                _fileManager = fileManager;
                fileSystemWatcher.EnableRaisingEvents = true;
                fileSystemWatcher.Changed += new FileSystemEventHandler(fileSystemWatcher_Changed);
                fileSystemWatcher.Created += new FileSystemEventHandler(fileSystemWatcher_Changed);
                fileSystemWatcher.Deleted += new FileSystemEventHandler(fileSystemWatcher_Changed);
                fileSystemWatcher.Renamed += new RenamedEventHandler(fileSystemWatcher_Changed);
            }

            //единый обработчик событий для FileSystemWatcher
            private void fileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
            {
                Refresh();
            }

            public void ChangeFolder(string fullname, string searchPattern)
            {
                if (!Directory.Exists(fullname))
                {
                    throw new FileManagerException();
                }
                fileSystemWatcher.Path = fullname;
                _currentFolder = fullname;
                _iterator = CreateIterator(searchPattern);
                _foldersBack.Push(_currentFolder);
                if (_fileManager.OnChangeFolder != null)
                {
                    _fileManager.OnChangeFolder(_fileManager, new FolderActionEventArgs(_iterator, _fileManager.IsPanelActive(this)));
                }
            }

            public void ChangeFolder(string fullname)
            {
                ChangeFolder(fullname, "*");
            }

            public void OpenMyComputerFolder()
            {
                _currentFolder = "My computer";
                _iterator = _fileManager.GetDrives();
                _foldersBack.Push(_currentFolder);
                if (_fileManager.OnChangeFolder != null)
                {
                    _fileManager.OnChangeFolder(_fileManager, new FolderActionEventArgs(_iterator, _fileManager.IsPanelActive(this)));
                }
            }

            public void Refresh()
            {
                //обновление равнозначно повторному входу в ту же папку
                ChangeFolder(_currentFolder);
            }

            public void GoBackward()
            {
                if (_foldersBack.Count != 0)
                {
                    _foldersForward.Push(_foldersBack.Pop());
                    ChangeFolder(_foldersBack.Pop());
                }
            }

            public void GoForward()
            {
                if (_foldersForward.Count != 0)
                {
                    ChangeFolder(_foldersForward.Pop());
                }
            }

            public void MakeAction(IFilePanelItem item)
            {
                item.MakeAction(this);
            }

            public void ApplyFileFilter(string searchPattern)
            {
                ChangeFolder(_currentFolder, searchPattern);
            }

            public void GoToParentFolder()
            {
                if (systemPaths.Contains(_currentFolder))
                {
                    return;
                }
                string root = Path.GetPathRoot(_currentFolder);
                //если не корень диска
                if (_currentFolder != root)
                {
                    DirectoryInfo parent = Directory.GetParent(_currentFolder.Substring(0, _currentFolder.Length - 1));
                    if (parent.FullName == root)    //slash
                    {
                        GoToRootFolder();
                    }
                    else
                    {
                        ChangeFolder(parent.FullName + "\\");
                    }
                }
                else
                {
                    OpenMyComputerFolder();
                }
            }

            public void GoToRootFolder()
            {
                //системные папки обрабатываются отдельно
                if (FileManager.systemPaths.Contains(_currentFolder))
                {
                    return;
                }
                _currentFolder = Directory.GetDirectoryRoot(_currentFolder);
                ChangeFolder(_currentFolder);
            }

            public IFilePanelMemento GetState()
            {
                return new FilePanelMemento(this);
            }

            public void SortPanelItems(Comparison<IFilePanelItem> comparer)
            {
                if (_fileManager.OnSortPanelItems != null)
                {
                    if (_iterator.Comparer != comparer)
                    {
                        _iterator.Comparer = comparer;
                    }
                    else
                    {
                        if (_iterator.Comparer != null)
                        {
                            _iterator.Reverse();
                        }
                    }
                    _fileManager.OnSortPanelItems(this, new FolderActionEventArgs(_iterator, _fileManager.IsPanelActive(this)));
                }
            }

            public IFolderIterator CreateIterator(string searchPattern, Comparison<IFilePanelItem> comparer)
            {
                FolderIterator itr = (FolderIterator)CreateIterator(searchPattern);
                itr.Comparer = comparer;
                return itr;
            }

            private bool FileIsNotHiddenOrSystem(FileSystemInfo file)
            {
                return ((file.Attributes & FileAttributes.Hidden) != FileAttributes.Hidden)
                    & ((file.Attributes & FileAttributes.System) != FileAttributes.System);
            }

            public IFolderIterator CreateIterator(string searchPattern)
            {
                DirectoryInfo[] _folders = new DirectoryInfo(_currentFolder).GetDirectories();
                FileInfo[] _files = new DirectoryInfo(_currentFolder).GetFiles(searchPattern);
                FolderIterator itr = new FolderIterator(_currentFolder);
                foreach (DirectoryInfo d in _folders)
                {
                    if (FileIsNotHiddenOrSystem(d))
                    {
                        itr.Add(FilePanelItemFactory.CreateFolder(d.Name, d.LastAccessTime, itr));
                    }
                }
                foreach (FileInfo f in _files)
                {
                    if (FileIsNotHiddenOrSystem(f))
                    {
                        itr.Add(FilePanelItemFactory.CreateFile(f.Name, f.Length, f.LastAccessTime, itr));
                    }
                }
                return itr;
            }
        }
    }
}